﻿using System;

using System.Collections.Generic;
using System.Text;
using System.Drawing;
using Silvermoon.OpenGL.Native;
using Silvermoon.OpenGL;
using System.Reflection;
using System.IO;
using Silvermoon.Utils;
using Silvermoon.Core;

namespace Silvermoon.Visuals
{
    /// <summary>
    /// A visual that uses a texture to render it by 4 quadrants of the image for the top/left, top/right bottom/left and bottom/right
    /// parts in 1:1 while filling the gaps with the difference between.
    /// </summary>
    /// <remarks>
    /// the arrays created have the follwowing graph:
    /// 
    /// 01  45
    /// 23  67
    /// 
    /// 89 CD
    /// AB EF
    /// 
    /// with 4 colors and
    /// 
    /// 00 01    04 05
    /// 02 03    06 07
    /// 
    /// 08 09    0C 0D
    /// 0A 0B   0E 0F
    /// 
    /// 10 11    14 15
    /// 12 13    16 17
    /// 
    /// 18 19    1C 1D
    /// 1A 1B   1E 1F
    /// 
    /// with 5 colors
    /// 
    /// where 0...G represent the order of each point in the index.
    /// </remarks>    
    public class BackgroundVisual : Visual
    {
        #region fields

        private Color[] colors = null;
        private int[] colorArray;
        short[] vertexArray;
        private Texture texture;
        private bool beveled;
        private RenderStyle style = RenderStyle.Plastic;


        #endregion
        #region ctor

        public BackgroundVisual()
            : base()
        {
            //texture = new Texture();
            //Stream s = Assembly.GetCallingAssembly().GetManifestResourceStream("Silvermoon.Images.menuBg.png");
            //texture.LoadFromStream(s);
        }


        #endregion
        #region props

        /// <summary>
        /// Gets or sets the style how to render the background.
        /// </summary>
        public RenderStyle Style { get { return style; } set { if (style != value) { style = value; Invalidate(); } } }

        /// <summary>
        /// Gets or sets the texture that contains the 4 quadrants to render.
        /// </summary>
        public Texture Texture { get { return texture; } set { if (texture != value) { texture = value; Invalidate(); } } }

        /// <summary>
        /// Gets or sets an array of colors which specify the gradient from top to bottom. If specified, 4 values are expected.
        /// </summary>
        public Color[] Colors
        {
            get { return colors; }
            set
            {
                if (value != colors)
                {
                    if (value != null && (value.Length != 4 && value.Length != 1 && value.Length != 5)) throw new IndexOutOfRangeException("ColorGradient expects 1, 4 or 5 Color values");
                    colors = value;
                    Invalidate();
                }
            }
        }

        /// <summary>
        /// Gets or sets whether the rectangle apears beveled. This works only in conjunction with 4 Colors specified. The default value is false.
        /// </summary>
        public bool Beveled { get { return beveled; } set { if (beveled != value) { beveled = value; Invalidate(); } } }

        #endregion
        #region constants

        static readonly float[] textureArrayPlastic = new float[]
            {
                0f,-1f,
                0.5f,-1f,
                0f,-0.5f,
                0.5f,-0.5f,

                0.5f,-1f,
                1f,-1f,
                0.5f,-0.5f,
                1f,-0.5f,

                0f,-0.5f,
                0.5f,-0.5f,
                0f,0f,
                0.5f,0f,

                0.5f,-0.5f,
                1f,-0.5f,
                0.5f,0f,
                1f,0f
            };

        static readonly float[] textureArrayChrome = new float[]
            {
                0f,-1f,
                .5f,-1f,
                .5f,-1f,
                1f,-1f,

                0f,-.5f,
                .5f,-.5f,
                .5f,-.5f,
                1f,-.5f,

                0f,-.5f,
                .5f,-.5f,
                .5f,-.5f,
                1f,-.5f,

                 0f,-.5f,
                .5f,-.5f,
                .5f,-.5f,
                1f,-.5f,

                0f,-.5f,
                .5f,-.5f,
                .5f,-.5f,
                1f,-.5f,

                0f,0f,
                .5f,0f,
                .5f,0f,
                1f,0f
            };



        static readonly short[] plasticIndexes = new short[]
        {
            3,1,0, 3,2,0,  // top left
            7,5,4, 7,6,4, // top right
            11,9,8, 11,10,8,       // bottom left
            15,13,12, 15,14,12, // bottom right
            6,4,1, 6,3,1,           //top center
            14,12,9,14,11,9,     // bottom center
            9,3,2,9,8,2,            //center left,
            13,7,6,13,12,6,      // center right    
            12,6,3,12,9,3          // center center
        };

        static readonly short[] chromeIndexes = new short[]
        {
            5,1,0,5,4,0,
            9,5,4,9,8,4,
            17,13,12,17,16,12,
            21,17,16,21,20,16,

            6,2,1,6,5,1,
            10,6,5,10,9,5,
            18,14,13,18,17,13,
            22,18,17,22,21,17,

            7,3,2,7,6,2,
            11,7,6,11,10,6,
            19,15,14,19,18,14,
            23,19,18,23,22,18
        };



        #endregion

        public void Initialize()
        {
            //if (texture == null) throw new ArgumentNullException("Texture", "Value not set.");
            CreateColorArray();
            if (style == RenderStyle.Chrome) CreateCromeVertexArray(); else CreatePlasticVertexArray();
        }

        private void CreateColorArray()
        {
            if (colors == null || colors.Length < 4) return;
            if (style == RenderStyle.Plastic) CreatePlasticColorArray(); else CreateChromeColorArray();
        }

        private void CreatePlasticColorArray()
        {
            if (colorArray == null || colorArray.Length != 16) colorArray = new int[16];
            colorArray[0] = colorArray[1] = colorArray[4] = colorArray[5] = ColorUtil.AlphaSwapBRColor(colors[0], Alpha).ToArgb();
            colorArray[2] = colorArray[3] = colorArray[6] = colorArray[7] = ColorUtil.AlphaSwapBRColor(colors[1], Alpha).ToArgb();
            colorArray[8] = colorArray[9] = colorArray[12] = colorArray[13] = ColorUtil.AlphaSwapBRColor(colors[2], Alpha).ToArgb();
            colorArray[10] = colorArray[11] = colorArray[14] = colorArray[15] = ColorUtil.AlphaSwapBRColor(colors[3], Alpha).ToArgb();

            if (beveled)
            {
                // this would be a 3d effect:
                colorArray[3] = colorArray[6] = colorArray[0];
                colorArray[9] = colorArray[12] = colorArray[10];
            }
        }

        private void CreateChromeColorArray()
        {
            if (colorArray == null || colorArray.Length != 24) colorArray = new int[24];

            int scale = texture != null ? 256 * texture.TextureSize.Height / Height : 128;
            Color c1 = ColorUtil.InterpolateColor(colors[0], colors[1], scale);
            Color c2 = ColorUtil.InterpolateColor(colors[3], colors[2], scale);
            colorArray[0] = colorArray[1] = colorArray[2] = colorArray[3] = ColorUtil.AlphaSwapBRColor(colors[0], Alpha).ToArgb();
            colorArray[4] = colorArray[5] = colorArray[6] = colorArray[7] = ColorUtil.AlphaSwapBRColor(c1, Alpha).ToArgb();
            colorArray[8] = colorArray[9] = colorArray[10] = colorArray[11] = ColorUtil.AlphaSwapBRColor(colors[1], Alpha).ToArgb();
            colorArray[12] = colorArray[13] = colorArray[14] = colorArray[15] = ColorUtil.AlphaSwapBRColor(colors[2], Alpha).ToArgb();
            colorArray[16] = colorArray[17] = colorArray[18] = colorArray[19] = ColorUtil.AlphaSwapBRColor(c2, Alpha).ToArgb();
            colorArray[20] = colorArray[21] = colorArray[22] = colorArray[23] = ColorUtil.AlphaSwapBRColor(colors[3], Alpha).ToArgb();
        }

        private void CreatePlasticVertexArray()
        {
            short width = (short)Size.Width;
            short height = (short)Size.Height;

            short tw = texture != null ? (short)(texture.TextureSize.Width / 2) : (short)(width / 3);
            short th = texture != null ? (short)(texture.TextureSize.Height / 2) : (short)(height / 3);

            vertexArray = new short[]
            {
                0,0,
                tw,0,
                0,th,
                tw,th,

                (short)(width-tw),0,
                width,0,
                (short)(width-tw),th,
                width,th,

                0,(short)(height-th),
                tw,(short)(height-th),
                0,(short)height,
                tw,(short)height,

                (short)(width-tw),(short)(height-th),
                width,(short)(height-th),
               (short)(width-tw),(short)height,
                width,(short)height
            };
        }

        private void CreateCromeVertexArray()
        {
            short width = (short)Size.Width;
            short height = (short)Size.Height;

            short tw = texture != null ? (short)(texture.TextureSize.Width / 2) : (short)(width / 4);
            short th = texture != null ? (short)(texture.TextureSize.Height / 2) : (short)(height / 4);

            short tw1 = (short)(width - tw);
            short th1 = (short)(height - th);
            short c = (short)(height / 2);
            vertexArray = new short[]
            {
                0,0, tw,0, tw1,0, width,0,
                0,th,tw,th,tw1,th,width,th,
                0,c,tw,c,tw1,c,width,c,
                0,c,tw,c,tw1,c,width,c,
                0,th1,tw,th1,tw1,th1,width,th1,
                0,height,tw,height,tw1,height,width,height
            };
        }


        protected override void CreateElements()
        {
            Initialize();
            base.CreateElements();
        }

        public override void Render(Renderer renderer)
        {
            //gl.Enable(Capability.Blend);

            int a = renderer.Alpha;
            if (Alpha != a)
            {
                Alpha = a;
                CreateColorArray();
            }

            if (texture != null)
            {
                gl.BindTexture(TargetTexture.Texture2D, texture.Name);
                //gl.ActiveTexture(TextureUnit.Texture0);
                //gl.ClientActiveTexture(TextureUnit.Texture0);
                //gl.EnableClientState(ClientStateArray.TextureCoord);
                //gl.Enable(Capability.Texture2D);
                gl.TexCoordPointer(2, glValueType.Float, 0, style == RenderStyle.Plastic ? textureArrayPlastic : textureArrayChrome);
            }
            else
            {
                gl.DisableClientState(ClientStateArray.TextureCoord);
                gl.Disable(Capability.Texture2D);
            }

            if (colorArray != null)
            {
                gl.EnableClientState(ClientStateArray.Color);
                gl.ColorPointer(4, glValueType.UnsignedByte, 0, colorArray);
            }

            if (colors.Length == 1)
            {
                Color c = colors[0];
                gl.Color4x(c.R << 8, c.G << 8, c.B << 8, (c.A * a));
            }
            short[] indexes = style == RenderStyle.Plastic ? plasticIndexes : chromeIndexes;
            //   gl.EnableClientState(ClientStateArray.Vertex);
            gl.VertexPointer(2, glValueType.Short, 0, vertexArray);
            gl.DrawElements(PrimitiveType.Triangles, indexes.Length, glValueType.UnsignedShort, indexes);
            if (texture == null)
            {
                gl.EnableClientState(ClientStateArray.TextureCoord);
                gl.Enable(Capability.Texture2D);
            }
        }
    }
}
